package redislib

import (
	"context"
	"net"
	"runtime"
	"src/zeus/common"
	"src/zeus/logger"
	"time"

	"github.com/redis/go-redis/v9"
)

var (
	//用来设置默认协程数
	moduleCap = runtime.NumCPU() * 10
)

// 设置redis地址,带端口
func RedisSetAddr(addr string) redisoptions {
	return func(mod *RedisAccess) {
		mod.addr = addr
	}
}

// 设置redis,auth(进入密码)
func RedisSetAuth(auth string) redisoptions {
	return func(mod *RedisAccess) {
		mod.auth = auth
	}
}

// 设置redis默认的indexdb
func RedisSetIndexDB(indexdb int) redisoptions {
	return func(mod *RedisAccess) {
		mod.indexdb = indexdb
	}
}

// 设置redis最大的空闲连接数，表示即使没有redis连接时依然可以保持N个空闲的连接，而不被清除，随时处于待命状态
func RedisSetMaxIdle(maxidle int) redisoptions {
	return func(mod *RedisAccess) {
		mod.maxIdle = maxidle
	}
}

// 最大的激活连接数，表示同时最多有N个连接 ，为0事表示没有限制
func RedisSetMaxActive(maxactive int) redisoptions {
	return func(mod *RedisAccess) {
		mod.maxActive = maxactive
	}
}

// 最大的空闲连接等待时间，超过此时间后，空闲连接将被关闭(秒)
func RedisSetIdleTimeout(idleTimeout time.Duration) redisoptions {
	return func(mod *RedisAccess) {
		mod.idleTimeout = idleTimeout * time.Second
	}
}

/*
出错重试次数,至少是1
*/
func RedisSetErrRetry(errRetry int) redisoptions {
	return func(mod *RedisAccess) {
		if errRetry > 1 {
			mod.errRetry = errRetry
		}
	}
}

// 设置错误处理回调
func RedisSeterrFunc(errF errFunc) redisoptions {
	return func(mod *RedisAccess) {
		mod.errFunc = errF
	}
}

type redisoptions func(mod *RedisAccess)

/*
错误处理回调
isend =true  	表示重试也失败了,最后一次的回调
isend =false 	后面还可以继续重试
返回值true表示如果可以，继续重试；
*/
type errFunc func(err error, isend bool, commandName string, args []interface{}) bool

// RedisAccess redis 管理器
type RedisAccess struct {
	DBConobj    *redis.Client //redis连接池
	addr        string        //连接字符串
	indexdb     int           //默认DB号
	auth        string        //连接密码
	maxIdle     int           //最大的空闲连接数，表示即使没有redis连接时依然可以保持N个空闲的连接，而不被清除，随时处于待命状态
	maxActive   int           //最大的激活连接数，表示同时最多有N个连接 ，为0事表示没有限制
	idleTimeout time.Duration //最大的空闲连接等待时间，超过此时间后，空闲连接将被关闭(秒)
	errRetry    int           //出错重试次数
	errFunc     errFunc
}

// NewRedisAccess 生成新的管理器
func NewRedisAccess(opts ...redisoptions) *RedisAccess {
	result := &RedisAccess{
		addr:        "127.0.0.1:6379",
		auth:        "",
		maxIdle:     moduleCap / 10,
		maxActive:   moduleCap,
		idleTimeout: 3600 * time.Second,
		errRetry:    1,
		errFunc:     nil,
	}
	for _, opt := range opts {
		opt(result)
	}
	result.DBConobj = redis.NewClient(&redis.Options{
		Addr: result.addr,
		Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
			logger.Debugf(logger.LogKey_Redis, "redis dial.%s", addr)
			netDialer := &net.Dialer{
				Timeout:   5 * time.Second,
				KeepAlive: 5 * time.Minute,
			}
			return netDialer.DialContext(ctx, network, addr)
		},
		OnConnect: func(ctx context.Context, cn *redis.Conn) error {
			logger.Debugf(logger.LogKey_Redis, "redis OnConnect.%s", cn)
			return nil
		},
		Password:     result.auth,
		DB:           result.indexdb,
		PoolSize:     result.maxActive,
		DialTimeout:  100 * time.Second,
		PoolTimeout:  100 * time.Second,
		ReadTimeout:  -1,
		WriteTimeout: -1,
		// MinIdleConns: result.maxIdle,
		// MaxIdleConns:    result.maxIdle,
		ConnMaxIdleTime: result.idleTimeout,
		// MaxActiveConns:  result.maxActive,
	})
	// result.DBConobj = redis.NewPool(result.dial, result.maxIdle)
	// result.DBConobj.MaxActive = result.maxActive
	// result.DBConobj.IdleTimeout = result.idleTimeout
	// result.DBConobj.Wait = true
	// result.DBConobj.TestOnBorrow = result.testOnBorrow
	return result
}

// GetConn 拿到一个可用的连接，你要在这句之后写上：defer conn.Close()
// 用来在使用完之后把连接放回池子里去
func (access *RedisAccess) GetConn() *RedisHandleModel {
	return &RedisHandleModel{
		Client: access.DBConobj,
		access: access,
		Ctx:    context.Background(),
	}
}

// Close 关闭池子，一般只有关服的时候才用到
func (access *RedisAccess) Close() {
	access.DBConobj.Close()
	// logger.Debug(logger.LogKey_Redis, "redis close.")

}

// RedisHandleModel 自己把reids的一些常用命令写在这里
type RedisHandleModel struct {
	*redis.Client
	Ctx    context.Context
	access *RedisAccess
}

func (rd *RedisHandleModel) Dispose() {
	rd.Close()
	// logger.Debug(logger.LogKey_Redis, "redis Dispose.")
}

// Redis的使用生命周期
func RedisUsing(rd *RedisHandleModel, f func(rd *RedisHandleModel)) {
	defer rd.Dispose()
	f(rd)
}

func (rd *RedisHandleModel) Close() error {
	return nil
}

func (rd *RedisHandleModel) HgetallByStringMap(key string) (result map[string]string, reserr error) {
	tmpli := make([]string, 0, 10)
	iter := rd.HScan(rd.Ctx, key, 0, "", 10).Iterator()
	for iter.Next(rd.Ctx) {
		tmpli = append(tmpli, iter.Val())
	}
	// if err != nil {
	// 	return nil, err
	// }
	// for cursor != 0 {
	// 	tmpli = append(tmpli, keys...)
	// 	keys, cursor, err = rd.HScan(rd.Ctx, key, cursor, "", 10).Result()
	// 	if err != nil {
	// 		return nil, err
	// 	}

	// }
	// tmpli = append(tmpli, keys...)
	result = make(map[string]string)
	for i := 0; i < len(tmpli); i += 2 {
		result[tmpli[i]] = tmpli[i+1]
	}
	return result, nil
}
func (rd *RedisHandleModel) HgetallByBytesMap(key string) (result map[string][]byte, reserr error) {
	tmpli := make([]string, 0, 10)
	iter := rd.HScan(rd.Ctx, key, 0, "", 10).Iterator()
	for iter.Next(rd.Ctx) {
		tmpli = append(tmpli, iter.Val())
	}
	// keys, cursor, err := rd.HScan(rd.Ctx, key, 0, "", 10).Result()
	// if err != nil {
	// 	return nil, err
	// }
	// for cursor != 0 {
	// 	tmpli = append(tmpli, keys...)
	// 	keys, cursor, err = rd.HScan(rd.Ctx, key, cursor, "", 10).Result()
	// 	if err != nil {
	// 		return nil, err
	// 	}

	// }
	// tmpli = append(tmpli, keys...)
	result = make(map[string][]byte)
	for i := 0; i < len(tmpli); i += 2 {
		result[tmpli[i]] = []byte(tmpli[i+1])
	}
	return result, nil
}

func (rd *RedisHandleModel) HgetallByInt64Map(key string) (result map[string]int64, reserr error) {
	tmpli := make([]string, 0, 10)
	iter := rd.HScan(rd.Ctx, key, 0, "", 10).Iterator()
	for iter.Next(rd.Ctx) {
		tmpli = append(tmpli, iter.Val())
	}
	// keys, cursor, err := rd.HScan(rd.Ctx, key, 0, "", 10).Result()
	// if err != nil {
	// 	return nil, err
	// }
	// for cursor != 0 {
	// 	tmpli = append(tmpli, keys...)
	// 	keys, cursor, err = rd.HScan(rd.Ctx, key, cursor, "", 10).Result()
	// 	if err != nil {
	// 		return nil, err
	// 	}

	// }
	// tmpli = append(tmpli, keys...)
	result = make(map[string]int64)
	for i := 0; i < len(tmpli); i += 2 {
		result[tmpli[i]] = common.NewString(tmpli[i+1]).ToInt64V()
	}
	return result, nil
}

type Args = map[string]interface{}
